/*********************************************************
          File Name:nontype_template_parameters.cpp
          Author: Abby Cin
          Mail: abbytsing@gmail.com
          Created Time: Wed 10 Feb 2016 09:08:04 PM CST
**********************************************************/

#include <iostream>

// 1. integer

template<int N>
int size()
{
    return N;
}

template<int N>
int array_size(const char *x)
{
    return N;
}

template<int N>
int array_size2(const char (&x)[N])
{
    return N;
}

// 2. pointer to object

template<void (*x)(void)>
void foo()
{
    x();
}

template<std::string *S>
void bar()
{
    std::cout << "bar: " << *S << std::endl;
    *S += " end!";
    std::cout << "append: " << *S << std::endl;
}

// 3. reference to object

template<std::string &S>
void baz()
{
    std::cout << "bar: " << S << std::endl;
    S += " end!";
    std::cout << "append: " << S << std::endl;
}
// must has static lifecycle, and can't be local variable or a pointer
// to a dynamic object or a reference to nontype template parameters 
std::string s{"test"};  

void func()
{
    std::cout << "void function\n";
}

template<char *S>
void foobar()
{
    std::cout << S << std::endl;
}

// external linkage
char *s6 = "test";
char s7[] = "test";

int main()
{
    //int n = 10;
    //std::cout << size<n>() << std::endl;    // wrong! parameter must be a const expression
    std::cout << size<10>() << std::endl;
    char y[] = "123";
    std::cout << array_size<4>(y) << std::endl;
    std::cout << array_size2(y) << std::endl;

    foo<func>();
    bar<&s>();
    baz<s>();
    //foobar<s6>();   // wrong! parameter must be a const expression
    foobar<s7>();

    return 0;
}
